using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using System.IO;
using System.IO.Compression;
using System.Drawing;

using JsonSharp.Values;
using System.Globalization;
using System.Web;
using System.Web.Caching;
using Barbar.MapReport.Common;
using Barbar.MapReport.Common.Drawing.ValueIndicators;
using System.Collections.ObjectModel;
using Barbar.MapReport.Common.Drawing.Commands;
using Barbar.MapReport.Common.Drawing;
using Barbar.MapReport.HtmlRender.Operations.Legends;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

namespace Barbar.MapReport.HtmlRender.Operations.ValueIndicators {
  [Serializable()]
  internal class ValueIndicatorCollection : Collection<FlatPoint> {
    private BaseProperties m_Properties;
    private int m_Radius;

    internal ValueIndicatorCollection(AddDynamicValueIndicatorsCommand command) {
      if (command == null)
        throw new ArgumentNullException("command");

      m_Properties = command.Properties;
      m_Radius = command.Radius;

      if (command.Points == null)
        throw new ArgumentException("Command doesn't include any points.", "command");

      foreach (BusinessGeoPoint point in command.Points) {
        FlatPoint indicator = new FlatPoint(point);
        Add(indicator);
      }
    }

    [NotNullValidator()]
    public BaseProperties Properties {
      get { return m_Properties; }
      set { m_Properties = value; }
    }

    public int Radius {
      get { return m_Radius; }
      set { m_Radius = value; }
    }

    public Collection<FlatPoint> GetIndicators(GeoBound bound) {
      Collection<FlatPoint> result = new Collection<FlatPoint>();
      foreach(FlatPoint indicator in this) {
        if (bound.ContainsPoint(indicator))
          result.Add(indicator);
      }
      return result;
    }

    internal ValueIndicatorAgregatorCollection GetAgregatorCollection(GeoBound bound, int zoomLevel, string handlerUrl) {
      MercatorProjection projection = new MercatorProjection(zoomLevel);
      LinkedList<FlatPoint> visibleIndicators = new LinkedList<FlatPoint>();
      foreach (FlatPoint indicator in GetIndicators(bound)) {
        indicator.ProjectedPoint = projection.ToPointD(indicator);
        visibleIndicators.AddLast(indicator);
      }

      int valuesCount = 1;
      BusinessProperties businessProperties = m_Properties as BusinessProperties;
      if (businessProperties != null)
        valuesCount = businessProperties.Descriptions.Length;

      ListProperties listProperties = m_Properties as ListProperties;

      ValueIndicatorAgregatorCollection agregators = new ValueIndicatorAgregatorCollection();
      while (visibleIndicators.Count > 0) {
        ValueIndicatorAgregator agregator = new ValueIndicatorAgregator(valuesCount, m_Radius, projection, handlerUrl);
        agregators.Add(agregator);
        Stack<FlatPoint> stack = new Stack<FlatPoint>();
        stack.Push(visibleIndicators.First.Value);
        visibleIndicators.RemoveFirst();
        while (stack.Count > 0) {
          FlatPoint currentIndicator = stack.Pop();
          LinkedListNode<FlatPoint> node = visibleIndicators.First;
          agregator.Add(currentIndicator);
          while (node != null) {
            LinkedListNode<FlatPoint> nextNode = node.Next;
            if (node.Value.ProjectedPoint.Substract(currentIndicator.ProjectedPoint).LengthInt32 < m_Radius * 2 &&
              (listProperties == null || listProperties.UnionDifferentValues ||
              node.Value.Value1 == currentIndicator.Value1)
              ) {
              stack.Push(node.Value);
              visibleIndicators.Remove(node);
            }
            node = nextNode;
          }
        }
        agregator.Recalculate();
      }

      if (businessProperties != null) {
        List<LegendRangeList> legendList = null; // m_Properties.BusinessProperties.CreateLegend(valueIndicators);
        for (int i = 0; i < legendList.Count; i++) {
          LegendRangeList legend = legendList[i];
          foreach (ValueIndicatorAgregator agregator in agregators) {
            agregator.SetColor(legend, i);
          }
        }
      }

      return agregators;
    }
  }
}
